#!/usr/bin/perl -w
# This script will run
# a compilation flow on a design.
# It takes an entity name and 
# calls quartus functions
# to incrementally work on the design
# use flags help control this flow
#
# by Eric Villasenor
# July 25, 2007

use Getopt::Std;
use strict;
use Cwd;

#####################################################################
# setup environment
#####################################################################
my $dir_main = cwd;
my $dir_proj;
my $dir_scpt;
my $dir_srce;
my $dir_mapp;
my $dir_simu;

my $time_now = localtime time;
my ($wkd, $month, $day, $te, $year) = split /\s+/, $time_now;
my $time_date = sprintf("%s %s %d, %4d", $te, $month, $day, $year);

# synthesize only?
my $inc_flow = 0; 
# do some downloading?
my $download = 0;

# entity name
my $entity;
# fc2 filename
my $fc2;
# pin assignemnt filename
my $pins;

# tcl script name
my $tcl_script;

# get name of this script
my @cmd_line = split /\//, $0;
my $script_name = $cmd_line[$#cmd_line];

# setup options
my %opts;
# get options
getopts('sdh', \%opts);
my $arg_size = @ARGV;

#####################################################################
# initialize
#####################################################################
sub Usage
{
  print <<AWOL;
Usage: $script_name [option] entity_name

Note: This program requires an up to date Makefile.

Options:
  -h          :   your getting it now
  -s          :   only runs synthesis
  -d          :   download sof file to board


AWOL
exit;
}

# do we have an entity name?
&Usage if ($arg_size != 1);

# check flags
$inc_flow = $opts{"s"} if (exists $opts{"s"});
$download = $opts{"d"} if (exists $opts{"d"});
&Usage if (exists $opts{"h"});

# are we in a project directory?
my @dir_loc = split /\//, $dir_main;
if ($dir_loc[$#dir_loc] !~ /(P|p)roject/)
{
  print "\nYou are not in a project directory.\nContinue?:";
  my $ans = <STDIN>;
  print "\n";
  exit if ($ans =~ /(n|N)/);
}

# make sure user < retarded; no extension on an entity
($entity = $ARGV[0]) =~ s/\..*$//;
# lower case bad idea
#$entity =~ tr/A-Z/a-z/;
$tcl_script = "$entity.tcl";
$fc2 = "$entity._fc2";
$pins = "$entity.pins";

# this is our project directory
# added a dot to hide the directory
# reduces clutter
$dir_proj = $dir_main . "/._$entity";

# check for scripts directory
$dir_scpt = $dir_main . "/scripts" if -d "scripts"
  or die "scripts directory not found!\n";

# check for source directory
$dir_srce = $dir_main . "/source" if -d "source"
  or die "source directory not found!\n";

# check for mapped directory
$dir_mapp = $dir_main . "/mapped" if -d "mapped"
  or die "mapped directory not found!\n";

# set where mapped files will be created
$dir_simu = $dir_proj . "/simulation/modelsim";

# check if proj exists, delete it then make it again
if (not $download)
{
  system("rm -rf $dir_proj") if (-d $dir_proj);
}
system("mkdir $dir_proj") if (not -d $dir_proj);

#####################################################################
# work 
#####################################################################

# try to download from .proj directory
if ($download and -e "$dir_proj/$entity.sof")
{
  print "\nProgramming board...\n";
  system("quartus_pgm -c \"USB-Blaster\" -m jtag -o p\\;$dir_proj/$entity.sof");
  exit;
}
# try to download from proj directory
elsif ($download and -e "$entity/$entity.sof")
{
  print "\nProgramming board...\n";
  system("quartus_pgm -c \"USB-Blaster\" -m jtag -o p\\;$dir_proj/$entity.sof");
  exit;
}
# fix things
elsif ($inc_flow and $download)
{
  print "\nYou have synthesize only and download options enabled\n";
  print "what are you thinking?....\n";
  print "disabling synthesize only\n\n";
  $inc_flow = 0;
}

# make the fc2 file
# check if make2fc2 fails i.e. returns anything but zero
system("make2fc2 $entity") == 0
	or die "Fix your Makefile.\n";
# check to see if we have a vhd file that matches entity
system("rm -rf $dir_proj") if (not -e "$dir_srce/$entity.vhd");
die "** $entity.vhd does not exist in your source directory.\n" if (not(-e "$dir_srce/$entity.vhd"));

# change directory to new project directory
chdir $dir_proj;

# copy memory file to project directory
system("cp -f $dir_main/*.hex $dir_proj &> /dev/null") == 0
	or print "\nCould not find a .hex file for memory in project directory.\nOnly worry if you are trying to synthesize a processor.\n";

# open script file
open TCL, "> $tcl_script"
  or die "Can not open $dir_proj/$tcl_script for writing.\n";

# make tcl script
# general heading
print TCL "# File: $tcl_script\n";
print TCL "# Generated on: $time_now\n\n";

# packages to load
print TCL "# Load Quartus II Tcl Project package\n";
print TCL "package require ::quartus::project\n\n";
print TCL "set need_to_close_project 0\n";
print TCL "set make_assignments 1\n\n";

# make project
print TCL "# Check that the right project is open\n";
print TCL "if {[is_project_open]} {\n";
print TCL "\tif {[string compare \$quartus(project) \"$entity\"]} {\n";
print TCL "\t\tputs \"Project $entity is not open\"\n";
print TCL "\t\tset make_assignments 0\n";
print TCL "\t}\n";
print TCL "} else {\n";
print TCL "\t# Only open if not already open\n";
print TCL "\tif {[project_exists $entity]} {\n";
print TCL "\t\tproject_open -revision $entity $entity\n";
print TCL "\t} else {\n";
print TCL "\t\tproject_new -revision $entity $entity\n";
print TCL "\t}\n";
print TCL "\tset need_to_close_project 1\n";
print TCL "}\n\n";

# assign values maybe more synthesis stuff here
print TCL "# Make assignments\n";
print TCL "if {\$make_assignments} {\n";
print TCL "\tset_global_assignment -name DEVICE EP2C35F672C6\n";
print TCL "\tset_global_assignment -name FAMILY \"Cyclone II\"\n";
print TCL "\tset_global_assignment -name ORIGINAL_QUARTUS_VERSION 6.1\n";
print TCL "\tset_global_assignment -name PROJECT_CREATION_TIME_DATE \"$time_date\"\n";
print TCL "\tset_global_assignment -name LAST_QUARTUS_VERSION 6.1\n";
print TCL "\tset_global_assignment -name EDA_SIMULATION_TOOL \"ModelSim (VHDL)\"\n";
print TCL "\tset_global_assignment -name EDA_INCLUDE_VHDL_CONFIGURATION_DECLARATION ON -section_id eda_simulation\n";
print TCL "\tset_global_assignment -name EDA_OUTPUT_DATA_FORMAT VHDL -section_id eda_simulation\n";
# test settings on
#print TCL "\tset_global_assignment -name OPTIMIZE_FAST_CORNER_TIMING ON\n";
#print TCL "\tset_global_assignment -name AUTO_RAM_RECOGNITION OFF\n";
print TCL "\tset_global_assignment -name CYCLONEII_M4K_COMPATIBILITY OFF\n";

# test settings off
print TCL "\tset_global_assignment -name EDA_GENERATE_FUNCTIONAL_NETLIST ON -section_id eda_simulation\n";
print TCL "\tset_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top\n";

# what vhdl files are there in this project
open VHDL, "$dir_scpt/$fc2"
  or die "Can not open $dir_scpt/$fc2\n";
while (<VHDL>)
{
	# this assumes source is in the driectory above this one
  print TCL "\tset_global_assignment -name VHDL_FILE ../$_\n";
}
# close file
close VHDL;

# set rest of settings
print TCL "\tset_global_assignment -name EDA_ENABLE_GLITCH_FILTERING ON -section_id eda_simulation\n";
print TCL "\tset_global_assignment -name MIN_CORE_JUNCTION_TEMP 0\n";
print TCL "\tset_global_assignment -name MAX_CORE_JUNCTION_TEMP 85\n";
print TCL "\tset_global_assignment -name POWER_PRESET_COOLING_SOLUTION \"23 MM HEAT SINK WITH 200 LFPM AIRFLOW\"\n";
print TCL "\tset_global_assignment -name POWER_BOARD_THERMAL_MODEL \"NONE (CONSERVATIVE)\"\n";
print TCL "\tset_global_assignment -name ENABLE_LOGIC_ANALYZER_INTERFACE OFF\n";

# setting instance unsure what this does
print TCL "\tset_instance_assignment -name PARTITION_HIERARCHY no_file_for_top_partition -to | -section_id Top\n";

# set pins and signal names
if (-e "$dir_scpt/$pins")
{
  open PINS, "$dir_scpt/$pins"
    or die "Can not open $dir_scpt/$pins\n";
  while (<PINS>)
  {
    next if (/^#/);
    next if (/To,Location/);
    s/\r//;
    my @pins = split /,/;
    chomp @pins;
    next if (@pins != 2);
    die "Bad pin file\n" if (@pins > 2);
    print TCL "\tset_location_assignment ";
    print TCL $pins[1];
    print TCL " -to ";
    print TCL $pins[0];
    print TCL "\n";
  }
  close PINS;
  print TCL "\n";
}
else
{
  print "not setting pin assignments no $pins file found.\n";
}

# commit assignments
print TCL "\t# Commit assignments\n";
print TCL "\texport_assignments\n\n";

# close project
print TCL "\t# Close project\n";
print TCL "\tif {\$need_to_close_project} {\n";
print TCL "\t\tproject_close\n";
print TCL "\t}\n";
print TCL "}";

# close script
close TCL;

# make project
system("quartus_sh -t $tcl_script");

# synthesize
system("quartus_map --read_settings_files=on --write_settings_files=on $entity -c $entity");

# fit to board
system("quartus_fit --read_settings_files=on --write_settings_files=on $entity -c $entity");

# timing analysis
system("quartus_tan --read_settings_files=on --write_settings_files=on $entity -c $entity");

# generate mapped vhd files
system("quartus_eda --read_settings_files=on --write_settings_files=on $entity -c $entity");
system("cp $dir_simu/$entity.vho $dir_mapp/$entity.vhd") if (-e "$dir_simu/$entity.vho") or
	print "Did not generate a mapped $entity\n";

# generate sof
system("quartus_asm --read_settings_files=on --write_settings_files=on $entity -c $entity") if (not $inc_flow);

# download sof to board
print "\nProgramming board...\n" if ($download and -e "$entity.sof");
system("quartus_pgm -c \"USB-Blaster\" -m jtag -o p\\;$entity.sof") if ($download and -e "$entity.sof");

# copy sof file to project directory
system("cp -f $dir_proj/$entity.sof $dir_main &> /dev/null") if (-e "$entity.sof");

#####################################################################
# clean up 
#####################################################################
# remove fc2 file
#system("rm -rf $dir_scpt/$fc2") if (-e "$dir_scpt/$fc2");
`rm -rf $dir_scpt/$fc2` if (-e "$dir_scpt/$fc2");

# remove pof file
#system("rm -rf $entity.pof") if (-e "$entity.pof");
`rm -rf $entity.pof` if (-e "$entity.pof");

# remove tcl file
`rm -rf $tcl_script` if (-e $tcl_script);

# set log file
chdir $dir_main;
`rm -rf compile.log`;
`touch compile.log`;
`chmod 600 compile.log`;
`cat $dir_proj/$entity.*.rpt > compile.log`;

